/*
 Автор: Швейкин Евгений Юрьевич

 Функциональное описание:     
    Модуль формирования и передачи пакета ethernet

    1 Пераметры 
    1.1 Размер очереди seq_sz_p

    
    1.1 CSR (регистры управления и статуса)
    Адрес   Имя                 Доступ      Описание битов
    0       status              R           [0] - Флаг пустого буфера
                                            [1] - Флаг переполнения буфера    
                                            [31:2] - Резерв

*/
//--------------------------------------------------------------------------------------------------

// ********************** МАКРОСЫ ******************************************************************
`include "eth_tx_def.vh"
        
// *********************** НАЧАЛО МОДУЛЯ ***********************************************************
module eth_tx
(

    input  logic            reset_n,
    input  logic            clk,
    
    // Сигналы ethernet 
    input  logic            coe_tx_clk,
    output logic [3:0]      coe_tx_data,
    output logic            coe_tx_en,
    output logic            coe_tx_er,
    
    // Управление модулем
    input  logic [5:0]      avs_csr_address,
    input  logic [5:0]      avs_csr_burstcount,
    input  logic            avs_csr_beginbursttransfer,
    input  logic            avs_csr_write,
    input  logic [31:0]     avs_csr_writedata,

    input  logic            avs_csr_read,
    output logic [31:0]     avs_csr_readdata,
    output logic            avs_csr_readdatavalid,
    
    output logic            avs_csr_waitrequest,

    // Сигналы чтения шины авалон
    input logic             avm_rd_waitrequest,
    
    output logic [31:0]     avm_rd_address,
    output logic [9:0]      avm_rd_burstcount,
    output logic            avm_rd_beginbursttransfer,

    input  logic [7:0]      avm_rd_readdata,
    input  logic            avm_rd_readdatavalid,
    output logic            avm_rd_read

);
//  ********************* КОНСТАНТЫ ****************************************************************


//  ********************* ТИПЫ ДАННЫХ **************************************************************
    typedef struct packed 
    {   
        logic [0:7][7:0]            preamble;
        logic [0:5][7:0]            dest;
        logic [0:5][7:0]            source;
        logic [0:1][7:0]            e_type;
    } eth_ii_header_t;
    
    typedef struct packed 
    {   
        logic [27:0]                    res;
        logic [3:0]                     cmd;        // команда
    } ctrl_t;
    
    // Статус
    typedef struct packed 
    {   
        logic [30:0]                    res;
        logic                           busy;       // Флаг выполнения команды
    } status_t;
    
    typedef struct packed 
    {   logic [7:0]                     data;
        logic                           wrreq;
        logic [10:0]                    wrusedw;
        logic                           wrfull;
        
        logic [3:0]                     q;
        logic                           rdreq;
        logic [11:0]                    rdusedw;
        logic                           rdempty;
    } data_fifo_t;
    
    // Состояния автомата отправки кадра
    enum logic [4:0]       
        {   
            IDLE_s              = 5'b00001,
            READ_START_s        = 5'b00010,
            SEND_HEAD_s         = 5'b00100,
            SEND_DATA_s         = 5'b01000,
            SEND_CRC_s          = 5'b10000
        } state_eth_tx, next_state, state_eth_tx_latch;
                        
//  ********************* ПЕРЕМЕННЫЕ ***************************************************************
    eth_ii_header_t                 eth_ii_header;
    data_fifo_t                     data_fifo;    
    status_t                        status;
    ctrl_t                          ctrl;
    
    // Для модуля avs
    logic [3:0]                     csr_address;
    logic                           rx_val;
    logic [31:0]                    rx_data; 
    logic                           tx_req;
    logic                           tx_val;
    logic [31:0]                    tx_data; 
    
    //
    logic [0:21][7:0]               header; 
    logic [0:3][7:0]                crc;
    
    logic [10:0]                    head_cnt;       // Счетчик байт заголовка
    logic [10:0]                    data_cnt;       // Счетчик байт данных
    logic [10:0]                    crc_cnt;        // Счетчик байт crc
    logic                           start;          // Запуск формирования пакета
    logic                           start_read;     // Запуск чтения    
    logic [10:0]                    data_size;      // Размер поля данных пакета
    logic [10:0]                    data_size_latch;// Размер поля данных пакета    
    
    logic [31:0]                    base_addr;      // Адрес откуда читать данные
    logic [7:0]                     payload;        // Байт данных пакета
    logic                           payload_req;    // Запрос данных из читалки
    
    logic                           soft_reset;     // Сброс
    
    logic                           busy;           // Модуль записи в память занят
    logic                           rdempty;        // Флаг пустого буфера читалки
    
    logic                           head_ena;       // Флаг заголовка пакета
    logic                           data_ena;       // Флаг данных пакета
    logic                           crc_ena;        // Флаг crc пакета
    logic                           crc_init;       // Сброс CRC
    logic                           state_change;   // Строб изменения состояния
// ********************* INITIAL *******************************************************************

//  ********************* ФУНКЦИИ ******************************************************************

//  ********************* ASSIGN *******************************************************************
    // Работаем пока фифо индексов не переполнилось
    assign status.res       = 31'd0;
    assign status.busy      = busy;
    assign data_fifo.rdreq  = ~data_fifo.rdempty;
    assign header           = eth_ii_header;
// ********************* ПРОЦЕССЫ ******************************************************************

    
// ************* ПРОЦЕССЫ НА ЧАСТОТЕ ETHERNET ******************************************************
    // 
    always_ff @(posedge coe_tx_clk)
    begin
        coe_tx_data <= data_fifo.q;
        coe_tx_en   <= data_fifo.rdreq;
        coe_tx_er   <= 1'b0;
    end

// ************* ПРОЦЕССЫ НА ЧАСТОТЕ ШИНЫ AVALON ***************************************************
    
    always_ff @(posedge clk)
    begin
        eth_ii_header.preamble  <= {8'h55, 8'h55, 8'h55, 8'h55, 8'h55, 8'h55, 8'h55, 8'hD5};
        //crc    <= {8'h11, 8'h22, 8'h33, 8'h44};
    end
    
       // Переключение состояний автомата (классическое описание)
    always_ff @(posedge clk,  negedge reset_n)
    begin
        if (!reset_n)
            state_eth_tx    <= IDLE_s;
        else if (soft_reset)
            state_eth_tx    <= IDLE_s;
        else
            state_eth_tx    <= next_state; 
    end
    
  // Определение следующего состояния автомата
    always_comb        
    begin    
        next_state    = state_eth_tx; 
        unique case (state_eth_tx)
            IDLE_s:  
            begin
                if (start)
                    next_state  = READ_START_s;   
                else;
            end     
                
            READ_START_s:
            begin
                next_state  = SEND_HEAD_s;
            end
                
            SEND_HEAD_s:
            begin
                if (head_cnt == 11'd21)
                    next_state  = SEND_DATA_s;
                else;
            end           
            
            SEND_DATA_s:
            begin
                if (data_cnt == data_size_latch - 11'd1)
                    next_state  = SEND_CRC_s;
                else;
            end  
            
            SEND_CRC_s:
            begin
                if (crc_cnt == 11'd3)
                    next_state  = IDLE_s;
                else;
            end  
        endcase
    end   
    
    // Реализация счетчика байт в разных полях
    always_ff @(posedge clk)
    begin
        if (start_read)
            data_size_latch <= data_size;
        else;
        
        head_cnt    <= 11'd0;
        if (head_ena)
            head_cnt    <= head_cnt + 11'd1;
        else;
        
        if (data_ena) begin
            if (payload_req)
                data_cnt    <= data_cnt + 11'd1;
            else;
        end
        else
            data_cnt    <= 11'd0;
        
        crc_cnt     <= 11'd0;
        if (crc_ena)
            crc_cnt     <= crc_cnt + 11'd1;
        else;        
    end
    
 
    // Описание выходов автомата 
    always_comb
    begin
        data_fifo.wrreq     = 1'b0;
        data_fifo.data      = 8'd0;
        start_read          = 1'b0;
        payload_req         = 1'b0;
        busy                = 1'b1;
        head_ena            = 1'b0;
        data_ena            = 1'b0;
        crc_ena             = 1'b0;
        unique case (state_eth_tx)
            IDLE_s:  
            begin
                busy        = 1'b0;
            end     
                
            READ_START_s:     
            begin
                start_read  = 1'b1;
            end
                
            SEND_HEAD_s:
            begin
                head_ena            = 1'b1;
                data_fifo.wrreq     = 1'b1;
                data_fifo.data      = header[head_cnt];
            end           
            
            SEND_DATA_s:
            begin
                data_ena    = 1'b1;
                if (!rdempty) begin
                    payload_req         = 1'b1;
                    data_fifo.wrreq     = 1'b1;
                    data_fifo.data      = payload;
                end
                else;
            end  
            
            SEND_CRC_s:
            begin
                crc_ena             = 1'b1;
                data_fifo.wrreq     = 1'b1;
                data_fifo.data      = crc[crc_cnt];//{crc[crc_cnt][3:0], crc[crc_cnt][7:4]};
            end           
        endcase
    end
    
    //Ёсформируем сброс счетчика CRC. Он должен быть выполнен на 7 байте заголовка (8'h5d).
    always_ff @(posedge clk)
    begin
        crc_init    <= 1'b0;
        if (head_cnt == 6)
            crc_init    <= 1'b1;
        else;
    end
    
    // Мультиплексор команд
    always_ff @(posedge clk)
    begin
        soft_reset  <= 1'b0;
        start       <= 1'b0;
        case (ctrl.cmd)
            1: soft_reset       <= 1'b1;
            2: start            <= 1'b1;
            default;
        endcase
    end
    
    always_ff @(posedge clk)
    begin
        ctrl.cmd <= 4'd0;
        if (rx_val) begin
            // Мультиплексор приемника данных по avs
            case (csr_address)
                0:  ;
                1:  ctrl                        <= rx_data;         // Команда управления
                2:  data_size                   <= rx_data[10:0];   // Размер поля данных пакета
                3:  base_addr                   <= rx_data;         // Адрес памяти, откуда брать данные
                4:  eth_ii_header.dest[2:5]     <= rx_data;         // Младшая часть MAC назначения
                5:  eth_ii_header.dest[0:1]     <= rx_data[15:0];   // Старшая часть MAC назначения
                6:  eth_ii_header.source[2:5]   <= rx_data;         // Младшая часть MAC источника
                7:  eth_ii_header.source[0:1]   <= rx_data[15:0];   // Старшая часть MAC источника
                8:  eth_ii_header.e_type        <= rx_data[15:0];   // Тип протокола L3
                default ;
            endcase;
        end
        else;
    end

    always_ff @(posedge clk)
    begin
        // Мультиплексор передатчика данных по avs
        if (tx_req) begin
            case (csr_address)
                0:  tx_data <= status;                      // Статус
                1:  tx_data <= ctrl;                        // Команда управления
                2:  tx_data <= {21'd0, data_size};          // Размер поля данных пакета
                3:  tx_data <= base_addr;                   // Адрес памяти, откуда брать данные
                4:  tx_data <= eth_ii_header.dest[0:3];            // Младшая часть MAC назначения
                5:  tx_data <= {16'd0, eth_ii_header.dest[4:5]};   // Старшая часть MAC назначения
                6:  tx_data <= eth_ii_header.source[0:3];          // Младшая часть MAC источника
                7:  tx_data <= {16'd0, eth_ii_header.source[4:5]}; // Старшая часть MAC источника
                8:  tx_data <= {16'd0, eth_ii_header.e_type};      // Тип протокола L3
                default ;
            endcase;
        end
        else;
    end

    

    
// ********************* ПОДКЛЮЧАЕМЫЕ МОДУЛИ *******************************************************

    // Упрощает шину авалон до сигналов адреса, данных и подтверждений
    ctrl_avs
    ctrl_avs_inst1
    (
        .reset_n                (reset_n),
        .clk                    (clk),

        .avs_address            (avs_csr_address),
        .avs_burstcount         (avs_csr_burstcount),
        .avs_beginbursttransfer (avs_csr_beginbursttransfer),
        .avs_write              (avs_csr_write),
        .avs_writedata          (avs_csr_writedata),

        .avs_read               (avs_csr_read),
        .avs_readdata           (avs_csr_readdata),
        .avs_readdatavalid      (avs_csr_readdatavalid),
                
        .avs_waitrequest        (avs_csr_waitrequest),
               
        .rx_val                 (rx_val),
        .rx_data                (rx_data),
                
        .tx_req                 (tx_req),
        .tx_val                 (tx_val),
        .tx_data                (tx_data),

        .addr                   (csr_address)
    );

    // Буфер FIFO для хранения выходных данных
    dcfifo_mixed_widths
    #(  
        .add_usedw_msb_bit      ("ON"),             // разрядность usedw на весь диапазон
        .lpm_showahead          ("ON"),             // режим ASK выключен
        .lpm_type               ("dcfifo_mixed_widths"),         // Разные частоты входа/выхода
        .overflow_checking      ("ON"),             // Проверка переполнения
        .underflow_checking     ("ON"),             // Проверка опустошения
        .use_eab                ("ON"), 
        .rdsync_delaypipe       (4),                
        .wrsync_delaypipe       (4),
        
        .lpm_numwords           (2048),             // Объем фифо на 1 пакет? (слов 8 бита)
        
        .lpm_width              (8),                // Разрядность входных данных 
        .lpm_widthu             (11),               // Разрядность адреса входа
        .lpm_width_r            (4),                // Разрядность выходных данных
        .lpm_widthu_r           (12)                // Разрядность адреса выхода
    )
    data_fifo_inst1
    (   
        .aclr                   (~reset_n | soft_reset),
    
        // Запись в фифо
        .wrclk                  (clk),
        .wrreq                  (data_fifo.wrreq),
        .data                   (data_fifo.data),
        .wrfull                 (data_fifo.wrfull),
        .wrusedw                (data_fifo.wrusedw),
      
        // Чтение из фифо
        .rdclk                  (coe_tx_clk),
        .rdreq                  (data_fifo.rdreq),
        .q                      (data_fifo.q),
        .rdusedw                (data_fifo.rdusedw),
        .rdempty                (data_fifo.rdempty)
    );

    amm_to_fifo_burst_bw
    #(
        .MAX_BURST              (64),
        .BUF_SZ                 (2048),
        .AMM_BW                 (8),
        .D_BW                   (8)
    )
    amm_to_fifo_burst_bw_inst1
    (   
        .reset_n                (reset_n & (~soft_reset)),

        
        // Управление модулем
        .clk_ctrl               (clk),
        .start_i                (start_read),
        .data_size_i            ({21'd0, data_size}),
        .base_addr_i            (base_addr),
        .busy_o                 (),
        .rdusedw                (),
        
        // Порты фифо
        .clk_fifo               (clk),
        .fifo_rdreq_i           (payload_req),        
        .fifo_q_o               (payload),
        .fifo_rdempty_o         (rdempty),
        
        // Сигналы шины master авалон
        .clk_amm                (clk),
        .avm_waitrequest        (avm_rd_waitrequest),
        .avm_readdata           (avm_rd_readdata),
        .avm_readdatavalid      (avm_rd_readdatavalid),

        .avm_address            (avm_rd_address),
        .avm_burstcount         (avm_rd_burstcount),
        .avm_beginbursttransfer (avm_rd_beginbursttransfer),
        .avm_read               (avm_rd_read)
    );
    
    crc32_d8
    crc32_d8_inst1
    (
        .init                   (crc_init),
        .clk                    (clk),
        
        .data                   (data_fifo.data), 
        .wre                    (data_fifo.wrreq & (~crc_ena)),
        
        .crc                    (crc)
    );
endmodule 